package com.gitee.srurl.snow.gen;

import cn.hutool.core.util.RandomUtil;
import com.gitee.srurl.snow.domain.SnowflakeConstant;
import com.gitee.srurl.snow.domain.SnowflakeData;
import lombok.extern.slf4j.Slf4j;

/**
 * @author zxw
 * @date 2023/4/22 13:32
 */
@Slf4j
public class DefaultSnowflake extends AbstractSnowflake {

    public DefaultSnowflake() {
        this(null);
    }

    /**
     * @param snowflakeData 初始化数据
     */
    public DefaultSnowflake(SnowflakeData snowflakeData) {
        super(-1, snowflakeData);
    }

    /**
     * @param snowflakeData 初始化数据
     */
    public DefaultSnowflake(long lastTimestamp, SnowflakeData snowflakeData) {
        super(lastTimestamp, snowflakeData);
    }

    @Override
    long doGetNextId() {

        long timestamp = genTime();
        if (this.cloakBackwards) {
            if (timestamp > this.clockBackwardsHandle.getLastTimestamp()) {
                // 说明时钟正常了 做一些清理操作(取消上报注册、清理等相关操作) 设置handle为null
                this.clockBackwardsHandle.doClean();
                this.clockBackwardsHandle = null;
                this.cloakBackwards = false;
            } else {
                // 使用handle获取ID
                return this.clockBackwardsHandle.nextId();
            }
        }
        // 第一次检测到时钟回拨
        if (timestamp < this.lastTimestamp) {
            this.firstCheckClockBackwards(timestamp);
            return this.clockBackwardsHandle.nextId();
        }

        if (timestamp == this.lastTimestamp) {
            final long sequence = (this.sequence + 1) & SnowflakeConstant.SEQUENCE_MASK;
            if (sequence == 0) {
                timestamp = tilNextMillis(this.lastTimestamp);
                if (timestamp == -1) {
                    // 说明等待的过程中 时钟回拨了
                    return this.clockBackwardsHandle.nextId();
                }
            }
            this.sequence = sequence;
        } else {
            if (randomSequenceLimit > 1) {
                sequence = RandomUtil.randomLong(randomSequenceLimit);
            } else {
                sequence = 0L;
            }
        }

        lastTimestamp = timestamp;

        return buildId(this.lastTimestamp);
    }
}
